package cn.xylin.zjwh.crack;

import android.content.Context;
import android.content.ContextWrapper;
import android.util.ArrayMap;
import java.lang.reflect.Method;
import dalvik.system.PathClassLoader;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XSharedPreferences;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

/**
 * @author XyLin
 * @date 2020/7/8 22:48:52
 * Go.java
 **/
public class Go implements IXposedHookLoadPackage {
    private static final String[] BLACK_STRS = {
            "/banner/", "ads", "taobao"
    };

    private static boolean isStringEquals(String equal, String... beEquals) {
        if (SettingActivity.isEmpty(equal) || beEquals == null) {
            return false;
        }
        for (String beContain : beEquals) {
            if (equal.equals(beContain)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam load) {
        if (SettingActivity.PKG.equals(load.packageName)) {
            XposedHelpers.findAndHookMethod(ContextWrapper.class, "attachBaseContext", Context.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) {
                    Context baseContext = (Context) param.args[0];
                    XSharedPreferences preferences = new XSharedPreferences(BuildConfig.APPLICATION_ID, SettingActivity.XML_SETTING);
                    preferences.makeWorldReadable();
                    if (preferences.getBoolean(SettingActivity.KEY_UPDATE_REBOOT, false)) {
                        try {
                            String apkFile = baseContext.getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0).sourceDir;
                            if (!SettingActivity.isEmpty(apkFile)) {
                                PathClassLoader classLoader = new PathClassLoader(apkFile, load.getClass().getClassLoader());
                                Class<?> clazz = Class.forName(Go.class.getName(), true, classLoader);
                                Method run = clazz.getDeclaredMethod("iWantRunning", Context.class, XSharedPreferences.class);
                                run.setAccessible(true);
                                run.invoke(null, baseContext, preferences);
                                return;
                            }
                        } catch (Exception ignore) {
                        }
                    }
                    iWantRunning(baseContext, preferences);
                }
            });
        } else if (BuildConfig.APPLICATION_ID.equals(load.packageName)) {
            XposedHelpers.findAndHookMethod(SettingActivity.class.getName(), load.classLoader, "getModuleState", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) {
                    param.setResult("模块已激活");
                }
            });
        }
    }

    private static void iWantRunning(final Context baseContext, XSharedPreferences preferences) {
        try {
            antiXposed(baseContext, preferences.getString(SettingActivity.KEY_CLASS_NAME, ""));
            if (preferences.getBoolean(SettingActivity.KEY_REMOVE_ADS, true)) {
                removeAds(baseContext);
            }
            XposedBridge.hookMethod(android.os.Environment.class.getDeclaredMethod("getExternalStorageDirectory"), new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) {
                    param.setResult(baseContext.getExternalFilesDir("傻逼生成的垃圾文件"));
                }
            });
            Class uploadClass = findClass("com.zjwh.android_wh_physicalfitness.entity.UploadFormatEntity", baseContext.getClassLoader());
            if (uploadClass != null) {
                if (preferences.getBoolean(SettingActivity.KEY_MODIFY_STEPS, false)) {
                    modifySteps(uploadClass, preferences.getInt(SettingActivity.KEY_MAX_STEPS, 0));
                }
                if (preferences.getBoolean(SettingActivity.KEY_MODIFY_TIME, false)) {
                    modifyTime(
                            uploadClass,
                            preferences.getLong(SettingActivity.KEY_START_TIME, System.currentTimeMillis()),
                            preferences.getLong(SettingActivity.KEY_END_TIME, System.currentTimeMillis())
                    );
                }
                if (preferences.getBoolean(SettingActivity.KEY_MODIFY_DIS, false)) {
                    modifyDis(uploadClass, preferences.getInt(SettingActivity.KEY_MAX_DIS, 0));
                }
            }
            if (preferences.getBoolean(SettingActivity.KEY_POINT_PASS, false)) {
                pointPass(baseContext);
            }
        } catch (Exception ignore) {
        }
    }

    private static void modifySteps(Class uploadClass, final int modifySteps) {
        XposedHelpers.findAndHookMethod(uploadClass, "setTotalSteps", int.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) {
                int currentSteps = (int) param.args[0];
                if (currentSteps < modifySteps) {
                    currentSteps += modifySteps;
                }
                param.args[0] = currentSteps;
            }
        });
    }

    private static void modifyTime(Class uploadClass, final long startTime, final long endTime) {
        XposedHelpers.findAndHookMethod(
                uploadClass, "setStartTime", long.class, new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        param.args[0] = startTime;
                    }
                }
        );
        XposedHelpers.findAndHookMethod(
                uploadClass, "setStopTime", long.class, new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        param.args[0] = endTime;
                    }
                }
        );
        XposedHelpers.findAndHookMethod(
                uploadClass, "setTotalTime", long.class, new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) {
                        param.args[0] = (endTime - startTime) / 1000L;
                    }
                }
        );
    }

    private static void modifyDis(Class uploadClass, final int modifyDis) {
        XC_MethodHook disModify = new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) {
                int currentDis = (int) param.args[0];
                if (currentDis < modifyDis) {
                    currentDis += modifyDis;
                }
                param.args[0] = currentDis;
            }
        };
        XposedHelpers.findAndHookMethod(uploadClass, "setTotalDis", int.class, disModify);
        XposedHelpers.findAndHookMethod(uploadClass, "setValidDis", int.class, disModify);
    }

    /**
     * 该方法用于点位全过，但由于新版策略变更，目前只支持3.2.5进行点位全过（3.2.4及之前应该不影响），并不再更新。
     * 为什么不更新这个功能了？因为我懒得分析它每个版本的混淆类名是什么
     *
     * @param baseContext context对象
     */
    private static void pointPass(Context baseContext) {
        Class<?> pointClass = findClass("com.zjwh.android_wh_physicalfitness.entity.database.FivePoint", baseContext.getClassLoader());
        if (pointClass != null) {
            Class<?> mapClass = findClass("com.zjwh.sw.map.baidu.OooO0OO", baseContext.getClassLoader());
            if (mapClass == null && "3.2.5".equals(SettingActivity.getZjwhVersionName(baseContext))) {
                mapClass = findClass("oo0ooOOO", baseContext.getClassLoader());
            }
            if (mapClass != null) {
                try {
                    final ArrayMap<Integer, double[]> pointLatLonMap = new ArrayMap<>();
                    XposedHelpers.findAndHookMethod(
                            pointClass, "getIsPass", new XC_MethodHook() {
                                @Override
                                protected void afterHookedMethod(MethodHookParam param) {
                                    int pointId = XposedHelpers.getIntField(param.thisObject, "id");
                                    if (!(boolean) param.getResult()) {
                                        double[] latLon = new double[2];
                                        latLon[0] = XposedHelpers.getDoubleField(param.thisObject, "lat");
                                        latLon[1] = XposedHelpers.getDoubleField(param.thisObject, "lon");
                                        pointLatLonMap.put(pointId, latLon);
                                    } else if (pointLatLonMap.containsKey(pointId)) {
                                        pointLatLonMap.remove(pointId);
                                    }
                                }
                            }
                    );
                    XposedBridge.hookMethod(mapClass.getDeclaredMethod("OooO00o", double.class, double.class, double.class, double.class), new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            double lat = (double) param.args[2], lon = (double) param.args[3];
                            for (double[] latLon : pointLatLonMap.values()) {
                                if (latLon[0] == lat && latLon[1] == lon) {
                                    param.setResult(0.0D);
                                    break;
                                }
                            }
                        }
                    });
                } catch (Exception ignore) {
                }
            }
        }
    }

    /**
     * 该方法用于过Xposed、病毒检测，但3.2.4版本及之后将病毒检测删掉了，所以重新调整了一下findClass方法
     * 如果后续有其它检测就需要有人来告诉我是什么检测，我才好研究能不能搞定。
     *
     * @param baseContext     context对象
     * @param customClassName 自定义Xposed检测类名
     */
    private static void antiXposed(Context baseContext, String customClassName) {
        Class<?> ijmClass = findClass("com.ijm.drisk.NativeTool", baseContext.getClassLoader());
        if (ijmClass != null) {
            for (Method method : ijmClass.getDeclaredMethods()) {
                if (isStringEquals(method.getName(), "checkHookAttack", "checkIsRunInVirtual", "checkIsSimulator", "checkMockApps", "checkRootStatus", "native_checkMockApps")) {
                    XposedBridge.hookMethod(method, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            if (isStringEquals("checkHookAttack", param.method.getName())) {
                                param.setResult(0);
                            } else {
                                param.setResult(false);
                            }
                        }
                    });
                    continue;
                }
                if (isStringEquals(method.getName(), "startCheckMockLocation")) {
                    XposedBridge.hookMethod(method, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            param.setResult(true);
                        }
                    });
                }
            }
        }
        String className = getClassName(customClassName, SettingActivity.getZjwhVersionName(baseContext));
        if (!SettingActivity.isEmpty(className)) {
            XC_MethodHook returnFalse = new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) {
                    param.setResult(false);
                }
            };
            Class<?> checkClass = findClass(className, baseContext.getClassLoader());
            if (checkClass != null) {
                for (Method method : checkClass.getDeclaredMethods()) {
                    if (boolean.class.equals(method.getReturnType())) {
                        XposedBridge.hookMethod(method, returnFalse);
                    }
                }
            }
        }
    }

    /**
     * 移除广告，但并没有什么用，该有的广告还是有，懒得去找关键点
     *
     * @param baseContext contexr对象
     */
    private static void removeAds(final Context baseContext) {
        try {
            XposedHelpers.findAndHookMethod(
                    findClass("com.qq.e.comm.managers.GDTADManager", baseContext.getClassLoader()),
                    "initWith", Context.class, String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            param.setResult(false);
                        }
                    }
            );
            XposedHelpers.findAndHookConstructor(
                    findClass("org.xutils.http.RequestParams", baseContext.getClassLoader()),
                    String.class, new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            String url = (String) param.args[0];
                            for (String blackStr : BLACK_STRS) {
                                if (url.contains(blackStr)) {
                                    param.args[0] = "http://127.0.0.1";
                                    break;
                                }
                            }
                        }
                    }
            );
            XposedBridge.hookMethod(
                    findClass("com.kwad.sdk.api.KsAdSDK", baseContext.getClassLoader()).getDeclaredMethod(
                            "init",
                            Context.class,
                            findClass("com.kwad.sdk.api.SdkConfig", baseContext.getClassLoader())
                    ),
                    new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            param.setResult(false);
                        }
                    }
            );
            XposedBridge.hookMethod(
                    findClass("com.bytedance.sdk.openadsdk.TTAdSdk", baseContext.getClassLoader()).getDeclaredMethod(
                            "init",
                            Context.class,
                            findClass("com.bytedance.sdk.openadsdk.TTAdConfig", baseContext.getClassLoader())
                    ),
                    new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) {
                            param.setThrowable(new NullPointerException());
                        }
                    }
            );
        } catch (Exception ignore) {
        }
    }

    private static String getClassName(String className, String version) {
        if (SettingActivity.isEmpty(className)) {
            switch (version) {
                case "3.1.3": {
                    return "oO0O0OOO";
                }
                case "3.1.5": {
                    return "oO0OOO0o";
                }
                case "3.1.7":
                case "3.1.8": {
                    return "oO0OO0oO";
                }
                case "3.1.9":
                case "3.2.0": {
                    return "oO0O00o0";
                }
                case "3.2.2":
                case "3.2.4": {
                    return "oO0OOoOO";
                }
                case "3.2.5": {
                    return "oOoOOo00";
                }
                default: {
                    break;
                }
            }
        }
        return className;
    }

    private static Class<?> findClass(String className, ClassLoader loader) {
        return XposedHelpers.findClassIfExists(className, loader);
    }
}
